//
// Created by 陈成 on 2019/9/19.
//

#include "InstB.h"

InstB::InstB(uint32_t pc, sc_bv<32> value, RegBank<REG_NUM> *r, RegBank<CSR_NUM> *csr, Mem *m) :
        Inst(pc, value, r, csr, m),
        imm   ((value.range(31, 31),
                value.range( 7,  7),
                value.range(30, 25),
                value.range(11, 8),
                sc_bv<1>(0))),
        rs2   (value.range(24, 20)),
        rs1   (value.range(19, 15)),
        funct3(value.range(14, 12)),
        opcode(value.range( 6,  0)) {

}

uint32_t
InstB::exec() {
    uint32_t pc_nxt = pc +4;
    switch (opcode.to_uint()) {
    case 0b0011111:
        pc_nxt = handleType0();
        break;
    default:
        assert(false);
        break;
    }

    return pc_nxt;
}

uint32_t
InstB::handleType0() {
    uint32_t u32_rs1 = r->read(rs1.to_uint()).to_uint();
    uint32_t u32_rs2 = r->read(rs2.to_uint()).to_uint();
    uint32_t i32_rs1 = r->read(rs1.to_uint()).to_int();
    uint32_t i32_rs2 = r->read(rs2.to_uint()).to_int();
    int32_t i32_imm = imm.to_int();

    uint32_t pc_nxt = pc + 4;
    switch (funct3.to_uint()) {
    case 0b000: // BEQ
        if (u32_rs1 == u32_rs2)
            pc_nxt = pc + i32_imm;
        break;
    case 0b001: // BNE
        if (u32_rs1 != u32_rs2)
            pc_nxt = pc + i32_imm;
        break;
    case 0b010: // BLT
        if (i32_rs1 < i32_rs2)
            pc_nxt = pc + i32_imm;
        break;
    case 0b011: // BLTU
        if (u32_rs1 < u32_rs2)
            pc_nxt = pc + i32_imm;
        break;
    case 0b100: // BGE
        if (i32_rs1 > i32_rs2)
            pc_nxt = pc + i32_imm;
        break;
    case 0b101: // BGEU
        if (u32_rs1 > u32_rs2)
            pc_nxt = pc + i32_imm;
        break;
    default:
        assert(false);
        break;
    }

    return pc_nxt;
}
